/**
 * @file regServer.c
 * @brief Sorgente che realizza un server che raccoglie le registrazioni dei client che vogliono effettuare la chat.
 *
 * Complete description of this file.
 *
 * @date Apr 16, 2012
 * @author Francesco Corvino
 */
#include <stdio.h>      /* standard C i/o facilities */
#include <stdlib.h>     /* needed for atoi() */
#include <unistd.h>  	/* defines STDIN_FILENO, system calls,etc */
#include <sys/types.h>  /* system data type definitions */
#include <sys/socket.h> /* socket specific definitions */
#include <netinet/in.h> /* INET constants and stuff */
#include <arpa/inet.h>  /* IP address conversion stuff */
#include <netdb.h>	/* gethostbyname */

#include <dynArray.h>
#include <userparam.h>

#define MAXBUF 1024*1024
#define CHATSTART_MESSAGE "start"

typedef struct sockaddr_in Address;

typedef struct registrazione {
	char name[20];
	Address address;
} Registrazione;


DynArray registrazioni;

/**
 * @brief Funzione che specifica il confronto tra due registrazioni
 *
 * @param a puntatore alla prima registrazione
 * @param b puntatore alla seconda registrazione
 * @return al momento si fa il confronto tra i nomi
 */
int Registrazione_cmp(const void * a, const void * b){
	Registrazione *A = (Registrazione*) a; Registrazione *B = (Registrazione*) b;
	return strcmp(A->name,B->name);
}

/**
 * @brief Funzione che gestisce la scrittura sullo standard output.
 */
void WriteMsg(char* name, char *msg) {
	printf("%s: %s \n", name, msg);
}

/**
 * @brief Inizializza i dati di rete restituendo il socket su cui effettuare
 * le comunicazioni.
 */
int StartNetData() {
	int ld;
	struct sockaddr_in skaddr;
	int length;

	/* create a socket IP protocol family (PF_INET) UDP protocol (SOCK_DGRAM) */
	if ((ld = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
		printf("Problem creating socket\n");
		exit(1);
	}

	skaddr.sin_family = AF_INET;
	skaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	skaddr.sin_port = htons(0);

	if (bind(ld, (struct sockaddr *) &skaddr, sizeof(skaddr)) < 0) {
		printf("Problem binding\n");
		exit(0);
	}

	/* find out what port we were assigned and print it out */

	length = sizeof(skaddr);
	if (getsockname(ld, (struct sockaddr *) &skaddr, &length) < 0) {
		printf("Error getsockname\n");
		exit(1);
	}

	/* port number's are network byte order, we have to convert to
	 host byte order before printing !
	 */
	printf("The server UDP port number is %d\n", ntohs(skaddr.sin_port));
	return ld;
}

/**
 * @brief Invia un messaggio al client.
 *
 * @param sd socket descriptor da utilizzare
 * @param msg
 * @param bytes
 * @param client
 */
void RegSend(int sd,char* msg, int bytes, Registrazione * client){
	int len = sizeof(Address);
	sendto(sd, msg, bytes, 0,
			(struct sockaddr*) &(client->address), len);
	printf("Messaggio <%s> inviato a %s \n",msg, client->name);
}

void RegSendError(int sd, Registrazione * client){
	char msg[] ="Error";
	RegSend(sd,msg,strlen(msg),client);
}

void RegSendList(int sd, Registrazione *client){
	char msg[MAXBUF];
	char * pointer = msg;
	int n=0,i=0;Registrazione *utente = NULL;
	DYNARRAY_FOREACH(&registrazioni,&i,utente){
		n= sprintf(pointer,"name=%s;address=%s;port=%d;\n",utente->name,
				inet_ntoa(utente->address.sin_addr),	ntohs(utente->address.sin_port));
		printf("aggiungo <%s> \n",pointer);
		pointer += n;
	}
	printf("messaggio <%s> \n",msg);
	RegSend(sd,msg,pointer-msg,client);
}

/**
 * @brief server main routine
 */
int main() {
	int socketFd;
	char msg[MAXBUF];
	DynArray_init(&registrazioni, Registrazione_cmp, sizeof(Registrazione));

	socketFd = StartNetData();

	while(1){
		/// - effettua delle operazioni di inizializzazione delle variabili
		memset(msg,0,MAXBUF);
		DynArray recvParams; Registrazione client;
		memset(&client, 0, sizeof(Registrazione));
		DynArray_init(&recvParams,UserParam_cmp,sizeof(UserParam));
		Address remote; int len= sizeof(Address);

		/// - si mette in attesa di un messaggio
		int n = recvfrom(socketFd, msg, MAXBUF, 0,
				(struct sockaddr *) &remote, &len);
		memcpy(&(client.address),&remote, sizeof(Address));

		/// - una volta ricevuto il messaggio ricava i parametri inseriti
		UserParam_fillArrDef(&recvParams, msg);

		/// - controlla se è inserito il parametro @b request e in base ad esso effettua:
		UserParam *request = UserParam_getFromDynArray(&recvParams,"request");
		if(request==NULL) {
			/// + request==NULL: richiesta non valida
			RegSendError(socketFd, &client);
		} else if(strcmp(request->value,"register")==0){
			/// + request==register: nuova registrazione
			UserParam * paramName = UserParam_getFromDynArray(&recvParams,"name");
			if(paramName!=NULL){
				strcpy(client.name,paramName->value);
				Registrazione *user = DynArray_get(&registrazioni,&client);
				if(user==NULL){
					// nuovo utente
					DynArray_add(&registrazioni,&client);
				} else{
					// utente già registrato

				}
			}
			RegSendError(socketFd,&client);
		}
		/// - effettua l'invio delle registrazioni
		RegSendList(socketFd,&client);
		/// - libera la memoria allocata
		DynArray_destroy(&recvParams);
	}
	return (0);
}

